﻿Public Class BigEndianBinaryReader

    Private Reader As Stream

    Private Buffer8 As Byte()
    Private Buffer4 As Byte()
    Private Buffer2 As Byte()
    Private Buffer1 As Byte()

    Protected Sub Read(Buffer As Byte(), Start As Integer, Length As Integer)
        If Reader.Read(Buffer, Start, Length) <> Length Then _
            Throw New MidiFileFormatException(Position, "Unexpected end of track stream")
    End Sub

    Public Sub New(Input As Stream)
        Reader = Input
        ReDim Buffer8(7)
        ReDim Buffer4(3)
        ReDim Buffer2(1)
        ReDim Buffer1(0)
    End Sub

    Public ReadOnly Property Position As Long
        Get
            Return Reader.Position
        End Get
    End Property

    Public ReadOnly Property EndOfStream As Boolean
        Get
            If Reader.ReadByte = -1 Then
                Return True
            Else
                Reader.Position -= 1
                Return False
            End If
        End Get
    End Property

    Public Function PeekByte() As Byte
        Read(Buffer1, 0, 1)
        Reader.Position -= 1
        Return Buffer1(0)
    End Function

    Public Function ReadByte() As Byte
        Read(Buffer1, 0, 1)
        Return Buffer1(0)
    End Function

    Public Function ReadInt64() As Long
        Read(Buffer8, 0, 8)
        Array.Reverse(Buffer8)
        Return BitConverter.ToInt64(Buffer8, 0)
    End Function

    Public Function ReadInt32() As Integer
        Read(Buffer4, 0, 4)
        Array.Reverse(Buffer4)
        Return BitConverter.ToInt32(Buffer4, 0)
    End Function

    Public Function ReadInt24() As Integer
        Buffer4(0) = 0
        Read(Buffer4, 1, 3)
        Array.Reverse(Buffer4)
        Return BitConverter.ToInt32(Buffer4, 0)
    End Function

    Public Function ReadInt16() As Short
        Read(Buffer2, 0, 2)
        Array.Reverse(Buffer2)
        Return BitConverter.ToInt16(Buffer2, 0)
    End Function

    Public Function ReadUInt64() As ULong
        Read(Buffer8, 0, 8)
        Array.Reverse(Buffer8)
        Return BitConverter.ToUInt64(Buffer8, 0)
    End Function

    Public Function ReadUInt32() As UInteger
        Read(Buffer4, 0, 4)
        Array.Reverse(Buffer4)
        Return BitConverter.ToUInt32(Buffer4, 0)
    End Function

    Public Function ReadUInt16() As UShort
        Read(Buffer2, 0, 2)
        Array.Reverse(Buffer2)
        Return BitConverter.ToUInt16(Buffer2, 0)
    End Function

    Public Function Read4CharString() As String
        Read(Buffer4, 0, 4)
        Return System.Text.Encoding.UTF8.GetString(Buffer4, 0, Buffer4.Length)
    End Function

    Public Function ReadVariableLengthInt() As Integer

        Dim Result As Integer = 0

        Do
            Read(Buffer1, 0, 1)
            Result = (Result << 7) Or (Buffer1(0) And &H7F)
        Loop Until (Buffer1(0) And &H80) = 0

        Return Result

    End Function

    Public Function ReadString() As String

        Dim Length As Integer = ReadVariableLengthInt()
        Dim Buffer(Length - 1) As Byte
        Read(Buffer, 0, Length)
        Return System.Text.Encoding.UTF8.GetString(Buffer, 0, Buffer.Length)

    End Function

    Public Function ReadBytes(Length As Integer) As Byte()

        Dim Result(Length - 1) As Byte
        Read(Result, 0, Length)
        Return Result

    End Function

    Public Sub Close()
        Reader.Close()
    End Sub

    Public Sub Dispose()
        Reader.Dispose()
    End Sub

End Class
